---
title: "集合类型 - 数组、字典和集合"
description: "学习 Swift 集合类型：数组、字典和集合，与 JavaScript 对比"
---

# 集合类型：数组、字典和集合

在本模块中，我们将探索 Swift 的集合类型，并与 JavaScript 的数组和对象结构进行对比。Swift 提供了三种主要的集合类型：数组（Arrays）、字典（Dictionaries）和集合（Sets），每种都具有强类型和强大的函数式编程能力。

## 目录

- [数组](#数组)
- [字典](#字典)
- [集合](#集合)
- [集合操作](#集合操作)
- [集合的函数式编程](#集合的函数式编程)
- [性能考虑](#性能考虑)
- [练习题](#练习题)
- [关键要点](#关键要点)

## 数组

Swift 中的数组是有序的元素集合，所有元素必须具有相同的类型，类似于 JavaScript 数组，但具有强类型特性。

### 数组声明和初始化

<UniversalEditor title="数组声明对比" compare={true}>
```javascript !! js
// JavaScript: 动态类型，可以混合不同类型
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, "hello", true, { name: "John" }];

// 创建空数组
let emptyArray = [];
let arrayWithSize = new Array(5); // 创建包含 5 个 undefined 元素的数组

// 包含重复值的数组
let repeated = Array(3).fill("hello");
```

```swift !! swift
// Swift: 强类型，所有元素必须是相同类型
var numbers: [Int] = [1, 2, 3, 4, 5]
var names: [String] = ["Alice", "Bob", "Charlie"]

// 类型推断
var scores = [85, 92, 78, 96] // Swift 推断为 [Int]

// 创建空数组
var emptyArray: [Int] = []
var emptyArray2 = [Int]()

// 包含重复值的数组
var repeated = Array(repeating: "hello", count: 3)
```
</UniversalEditor>

### 数组访问和修改

<UniversalEditor title="数组访问和修改" compare={true}>
```javascript !! js
// JavaScript 数组访问
let fruits = ["apple", "banana", "orange"];
console.log(fruits[0]); // "apple"
console.log(fruits.length); // 3

// 添加元素
fruits.push("grape"); // 添加到末尾
fruits.unshift("kiwi"); // 添加到开头
fruits.splice(2, 0, "mango"); // 在索引 2 处插入

// 删除元素
let last = fruits.pop(); // 删除并返回最后一个
let first = fruits.shift(); // 删除并返回第一个
fruits.splice(1, 1); // 删除索引 1 处的 1 个元素

// 检查边界
if (fruits[5]) {
    console.log("元素存在");
} else {
    console.log("索引越界");
}
```

```swift !! swift
// Swift 数组访问
var fruits = ["apple", "banana", "orange"]
print(fruits[0]) // "apple"
print(fruits.count) // 3

// 添加元素
fruits.append("grape") // 添加到末尾
fruits.insert("kiwi", at: 0) // 在开头插入
fruits.insert("mango", at: 2) // 在索引 2 处插入

// 删除元素
let last = fruits.removeLast() // 删除并返回最后一个
let first = fruits.removeFirst() // 删除并返回第一个
let removed = fruits.remove(at: 1) // 删除并返回索引 1 处的元素

// 安全的数组访问
if fruits.indices.contains(5) {
    print("元素存在: \(fruits[5])")
} else {
    print("索引越界")
}

// 使用 first 和 last 属性
if let firstFruit = fruits.first {
    print("第一个水果: \(firstFruit)")
}
if let lastFruit = fruits.last {
    print("最后一个水果: \(lastFruit)")
}
```
</UniversalEditor>

### 数组切片和范围操作

<UniversalEditor title="数组切片和范围操作" compare={true}>
```javascript !! js
// JavaScript 数组切片
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 切片 (start, end)
let slice1 = numbers.slice(2, 5); // [3, 4, 5]
let slice2 = numbers.slice(-3); // [8, 9, 10]
let slice3 = numbers.slice(0, -2); // [1, 2, 3, 4, 5, 6, 7, 8]

// 创建范围
let range = Array.from({length: 5}, (_, i) => i + 1); // [1, 2, 3, 4, 5]
```

```swift !! swift
// Swift 数组切片
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 使用范围进行切片
let slice1 = numbers[2..<5] // [3, 4, 5]
let slice2 = numbers[7...] // [8, 9, 10]
let slice3 = numbers[..<8] // [1, 2, 3, 4, 5, 6, 7, 8]

// 创建范围
let range = Array(1...5) // [1, 2, 3, 4, 5]
let range2 = Array(1..<6) // [1, 2, 3, 4, 5]

// 使用 stride 创建自定义范围
let stepRange = Array(stride(from: 0, to: 10, by: 2)) // [0, 2, 4, 6, 8]
```
</UniversalEditor>

## 字典

Swift 中的字典是键值对集合，类似于 JavaScript 对象，但对键和值都有强类型要求。

### 字典声明和初始化

<UniversalEditor title="字典声明对比" compare={true}>
```javascript !! js
// JavaScript 对象/字典
let person = {
    name: "John",
    age: 30,
    city: "New York"
};

// 创建空对象
let emptyObject = {};

// 具有计算属性的对象
let key = "dynamicKey";
let dynamicObject = {
    [key]: "dynamic value",
    staticKey: "static value"
};

// 嵌套对象
let user = {
    profile: {
        name: "Alice",
        email: "alice@example.com"
    },
    settings: {
        theme: "dark",
        notifications: true
    }
};
```

```swift !! swift
// Swift 字典
var person: [String: Any] = [
    "name": "John",
    "age": 30,
    "city": "New York"
]

// 类型安全的字典
var scores: [String: Int] = [
    "Alice": 85,
    "Bob": 92,
    "Charlie": 78
]

// 创建空字典
var emptyDict: [String: Int] = [:]
var emptyDict2 = [String: Int]()

// 具有计算属性的字典
let key = "dynamicKey"
var dynamicDict: [String: String] = [
    key: "dynamic value",
    "staticKey": "static value"
]

// 嵌套字典
var user: [String: [String: Any]] = [
    "profile": [
        "name": "Alice",
        "email": "alice@example.com"
    ],
    "settings": [
        "theme": "dark",
        "notifications": true
    ]
]
```
</UniversalEditor>

### 字典访问和修改

<UniversalEditor title="字典访问和修改" compare={true}>
```javascript !! js
// JavaScript 对象访问
let person = { name: "John", age: 30 };

// 访问属性
console.log(person.name); // "John"
console.log(person["age"]); // 30
console.log(person.address); // undefined

// 安全访问（可选链）
console.log(person.address?.street); // undefined

// 添加/修改属性
person.city = "New York";
person["occupation"] = "Developer";

// 删除属性
delete person.age;

// 检查属性是否存在
if ("name" in person) {
    console.log("姓名存在");
}

// 获取键和值
let keys = Object.keys(person);
let values = Object.values(person);
let entries = Object.entries(person);
```

```swift !! swift
// Swift 字典访问
var person: [String: Any] = ["name": "John", "age": 30]

// 访问值
print(person["name"] as? String ?? "Unknown") // "John"
print(person["age"] as? Int ?? 0) // 30

// 安全访问（可选绑定）
if let name = person["name"] as? String {
    print("姓名: \(name)")
}

// 添加/修改值
person["city"] = "New York"
person["occupation"] = "Developer"

// 删除值
person.removeValue(forKey: "age")
person["age"] = nil // 替代方法

// 检查键是否存在
if person["name"] != nil {
    print("姓名存在")
}

// 获取键和值
let keys = Array(person.keys)
let values = Array(person.values)

// 遍历字典
for (key, value) in person {
    print("\(key): \(value)")
}
```
</UniversalEditor>

## 集合

Swift 中的集合是无序的唯一元素集合，类似于 JavaScript 的 Set，但具有强类型特性。

### 集合声明和操作

<UniversalEditor title="集合声明和操作" compare={true}>
```javascript !! js
// JavaScript Set
let numbers = new Set([1, 2, 3, 4, 5]);
let fruits = new Set(["apple", "banana", "orange"]);

// 创建空 Set
let emptySet = new Set();

// 添加元素
numbers.add(6);
fruits.add("grape");

// 删除元素
numbers.delete(1);
fruits.delete("apple");

// 检查成员关系
console.log(numbers.has(3)); // true
console.log(fruits.has("mango")); // false

// Set 大小
console.log(numbers.size); // 5

// 转换为数组
let numbersArray = Array.from(numbers);
```

```swift !! swift
// Swift Set
var numbers: Set<Int> = [1, 2, 3, 4, 5]
var fruits: Set<String> = ["apple", "banana", "orange"]

// 创建空 Set
var emptySet: Set<Int> = []
var emptySet2 = Set<Int>()

// 添加元素
numbers.insert(6)
fruits.insert("grape")

// 删除元素
numbers.remove(1)
fruits.remove("apple")

// 检查成员关系
print(numbers.contains(3)) // true
print(fruits.contains("mango")) // false

// Set 计数
print(numbers.count) // 5

// 转换为数组
let numbersArray = Array(numbers)
```
</UniversalEditor>

### 集合操作

<UniversalEditor title="集合操作" compare={true}>
```javascript !! js
// JavaScript Set 操作
let set1 = new Set([1, 2, 3, 4]);
let set2 = new Set([3, 4, 5, 6]);

// 并集
let union = new Set([...set1, ...set2]);

// 交集
let intersection = new Set(
    [...set1].filter(x => set2.has(x))
);

// 差集
let difference = new Set(
    [...set1].filter(x => !set2.has(x))
);

// 子集检查
let isSubset = [...set1].every(x => set2.has(x));

// 超集检查
let isSuperset = [...set2].every(x => set1.has(x));
```

```swift !! swift
// Swift Set 操作
var set1: Set<Int> = [1, 2, 3, 4]
var set2: Set<Int> = [3, 4, 5, 6]

// 并集
let union = set1.union(set2)

// 交集
let intersection = set1.intersection(set2)

// 差集
let difference = set1.subtracting(set2)

// 对称差集
let symmetricDiff = set1.symmetricDifference(set2)

// 子集和超集检查
let isSubset = set1.isSubset(of: set2)
let isSuperset = set1.isSuperset(of: set2)
let isDisjoint = set1.isDisjoint(with: set2)
```
</UniversalEditor>

## 集合操作

Swift 集合提供了强大的函数式编程方法，类似于 JavaScript 数组方法。

### 函数式编程方法

<UniversalEditor title="集合的函数式编程" compare={true}>
```javascript !! js
// JavaScript 函数式编程
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Map
let doubled = numbers.map(x => x * 2);

// Filter
let evens = numbers.filter(x => x % 2 === 0);

// Reduce
let sum = numbers.reduce((acc, x) => acc + x, 0);

// Find
let firstEven = numbers.find(x => x % 2 === 0);

// Some/Every
let hasEven = numbers.some(x => x % 2 === 0);
let allPositive = numbers.every(x => x > 0);

// FlatMap
let nested = [[1, 2], [3, 4], [5, 6]];
let flattened = nested.flatMap(x => x);

// 链式调用
let result = numbers
    .filter(x => x % 2 === 0)
    .map(x => x * 2)
    .reduce((acc, x) => acc + x, 0);
```

```swift !! swift
// Swift 函数式编程
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// Map
let doubled = numbers.map { $0 * 2 }

// Filter
let evens = numbers.filter { $0 % 2 == 0 }

// Reduce
let sum = numbers.reduce(0) { $0 + $1 }

// First（相当于 find）
let firstEven = numbers.first { $0 % 2 == 0 }

// Contains（相当于 some）
let hasEven = numbers.contains { $0 % 2 == 0 }
let allPositive = numbers.allSatisfy { $0 > 0 }

// FlatMap（现在 compactMap 用于可选类型，flatMap 用于序列）
let nested = [[1, 2], [3, 4], [5, 6]]
let flattened = nested.flatMap { $0 }

// 链式调用
let result = numbers
    .filter { $0 % 2 == 0 }
    .map { $0 * 2 }
    .reduce(0) { $0 + $1 }

// 额外的 Swift 方法
let sorted = numbers.sorted()
let reversed = numbers.reversed()
let shuffled = numbers.shuffled()
```
</UniversalEditor>

### 集合性能

<UniversalEditor title="集合性能对比" compare={true}>
```javascript !! js
// JavaScript 集合性能考虑
let array = [1, 2, 3, 4, 5];

// 数组操作性能
// Push/pop: O(1)
array.push(6);
array.pop();

// Unshift/shift: O(n) - 需要移动所有元素
array.unshift(0);
array.shift();

// 索引访问: O(1)
let element = array[2];

// 搜索: O(n)
let index = array.indexOf(3);

// 对象属性访问: O(1) 平均
let obj = { a: 1, b: 2, c: 3 };
let value = obj.a;

// Set 操作: O(1) 平均
let set = new Set([1, 2, 3]);
set.has(2); // O(1) 平均
```

```swift !! swift
// Swift 集合性能考虑
var array = [1, 2, 3, 4, 5]

// 数组操作性能
// Append/removeLast: O(1) 摊销
array.append(6)
array.removeLast()

// Insert/remove at beginning: O(n) - 需要移动元素
array.insert(0, at: 0)
array.removeFirst()

// 索引访问: O(1)
let element = array[2]

// 搜索: O(n)
if let index = array.firstIndex(of: 3) {
    print("在索引处找到: \(index)")
}

// 字典访问: O(1) 平均
var dict: [String: Int] = ["a": 1, "b": 2, "c": 3]
let value = dict["a"]

// Set 操作: O(1) 平均
var set: Set<Int> = [1, 2, 3]
set.contains(2) // O(1) 平均

// Swift 特定优化
// ContiguousArray 用于更好的性能
var fastArray = ContiguousArray<Int>([1, 2, 3, 4, 5])

// 大集合的惰性求值
let lazyResult = numbers.lazy
    .filter { $0 % 2 == 0 }
    .map { $0 * 2 }

// 只在访问时计算
let firstLazyResult = lazyNumbers.first { $0 > 1000 }
```
</UniversalEditor>

## 集合的函数式编程

Swift 的集合类型支持强大的函数式编程模式。

### 高级函数式操作

<UniversalEditor title="高级函数式操作" compare={true}>
```javascript !! js
// JavaScript 高级函数式编程
let students = [
    { name: "Alice", grade: 85, age: 20 },
    { name: "Bob", grade: 92, age: 19 },
    { name: "Charlie", grade: 78, age: 21 },
    { name: "Diana", grade: 95, age: 20 }
];

// 复杂过滤和映射
let topStudents = students
    .filter(student => student.grade >= 90)
    .map(student => ({
        name: student.name,
        grade: student.grade,
        status: "Excellent"
    }));

// 分组
let groupedByAge = students.reduce((groups, student) => {
    const age = student.age;
    if (!groups[age]) groups[age] = [];
    groups[age].push(student);
    return groups;
}, {});

// 多条件排序
let sortedStudents = students.sort((a, b) => {
    if (a.grade !== b.grade) {
        return b.grade - a.grade; // 按成绩降序
    }
    return a.name.localeCompare(b.name); // 然后按姓名
});
```

```swift !! swift
// Swift 高级函数式编程
struct Student {
    let name: String
    let grade: Int
    let age: Int
}

let students = [
    Student(name: "Alice", grade: 85, age: 20),
    Student(name: "Bob", grade: 92, age: 19),
    Student(name: "Charlie", grade: 78, age: 21),
    Student(name: "Diana", grade: 95, age: 20)
]

// 复杂过滤和映射
let topStudents = students
    .filter { $0.grade >= 90 }
    .map { student in
        (name: student.name, grade: student.grade, status: "Excellent")
    }

// 分组
let groupedByAge = Dictionary(grouping: students) { $0.age }

// 多条件排序
let sortedStudents = students.sorted { first, second in
    if first.grade != second.grade {
        return first.grade > second.grade // 按成绩降序
    }
    return first.name < second.name // 然后按姓名
}

// 额外的 Swift 函数式特性
// CompactMap 用于可选类型处理
let optionalNumbers = ["1", "2", "three", "4", "5"]
let validNumbers = optionalNumbers.compactMap { Int($0) }

// Prefix 和 suffix
let firstThree = Array(students.prefix(3))
let lastTwo = Array(students.suffix(2))

// Partition
var mutableStudents = students
let partitionIndex = mutableStudents.partition { $0.grade >= 85 }
let highAchievers = Array(mutableStudents[..<partitionIndex])
let others = Array(mutableStudents[partitionIndex...])
```
</UniversalEditor>

## 性能考虑

理解性能特征有助于编写高效的 Swift 代码。

### 内存和性能优化

<UniversalEditor title="性能优化" compare={true}>
```javascript !! js
// JavaScript 性能考虑
// 数组性能
let largeArray = new Array(1000000).fill(0);

// 高效的数组操作
// 对大数组使用 push/pop 而不是 unshift/shift
let efficientArray = [];
for (let i = 0; i < 1000; i++) {
    efficientArray.push(i); // O(1)
}

// 对象属性访问优化
let optimizedObject = {};
// 预定义属性以获得更好的性能
optimizedObject.prop1 = 1;
optimizedObject.prop2 = 2;

// Set 用于唯一值
let uniqueValues = new Set();
for (let i = 0; i < 1000; i++) {
    uniqueValues.add(i % 100); // 只有 100 个唯一值
}
```

```swift !! swift
// Swift 性能考虑
// 数组性能
var largeArray = Array(repeating: 0, count: 1_000_000)

// 使用 ContiguousArray 获得更好的性能
var fastArray = ContiguousArray<Int>()

// 高效的数组操作
for i in 0..<1000 {
    fastArray.append(i) // O(1) 摊销
}

// 预分配容量以获得更好的性能
var optimizedArray = [Int]()
optimizedArray.reserveCapacity(1000) // 预分配内存

// 字典优化
var optimizedDict = [String: Int]()
optimizedDict.reserveCapacity(100) // 预分配内存

// Set 用于唯一值
var uniqueValues = Set<Int>()
for i in 0..<1000 {
    uniqueValues.insert(i % 100) // 只有 100 个唯一值
}

// 大集合的惰性求值
let lazyNumbers = (1...1_000_000).lazy
    .filter { $0 % 2 == 0 }
    .map { $0 * 2 }

// 只在访问时计算
let firstLazyResult = lazyNumbers.first { $0 > 1000 }
```
</UniversalEditor>

## 练习题

### 练习 1：数组操作
创建一个函数，接受一个整数数组，返回一个新数组，只包含偶数并翻倍。

<UniversalEditor title="练习 1 解答" compare={true}>
```javascript !! js
// JavaScript 解答
function getDoubledEvens(numbers) {
    return numbers
        .filter(num => num % 2 === 0)
        .map(num => num * 2);
}

// 测试
let testNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(getDoubledEvens(testNumbers)); // [4, 8, 12, 16, 20]
```

```swift !! swift
// Swift 解答
func getDoubledEvens(_ numbers: [Int]) -> [Int] {
    return numbers
        .filter { $0 % 2 == 0 }
        .map { $0 * 2 }
}

// 测试
let testNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(getDoubledEvens(testNumbers)) // [4, 8, 12, 16, 20]
```
</UniversalEditor>

### 练习 2：字典操作
创建一个函数，统计字符串中每个字符的频率。

<UniversalEditor title="练习 2 解答" compare={true}>
```javascript !! js
// JavaScript 解答
function countCharacters(str) {
    const charCount = {};
    for (let char of str) {
        charCount[char] = (charCount[char] || 0) + 1;
    }
    return charCount;
}

// 测试
console.log(countCharacters("hello")); // { h: 1, e: 1, l: 2, o: 1 }
```

```swift !! swift
// Swift 解答
func countCharacters(_ str: String) -> [Character: Int] {
    var charCount: [Character: Int] = [:]
    for char in str {
        charCount[char, default: 0] += 1
    }
    return charCount
}

// 测试
print(countCharacters("hello")) // ["h": 1, "e": 1, "l": 2, "o": 1]
```
</UniversalEditor>

### 练习 3：集合操作
创建一个函数，找到两个数组的交集（共同元素）。

<UniversalEditor title="练习 3 解答" compare={true}>
```javascript !! js
// JavaScript 解答
function findIntersection(arr1, arr2) {
    const set1 = new Set(arr1);
    const set2 = new Set(arr2);
    return [...set1].filter(x => set2.has(x));
}

// 测试
let array1 = [1, 2, 3, 4, 5];
let array2 = [3, 4, 5, 6, 7];
console.log(findIntersection(array1, array2)); // [3, 4, 5]
```

```swift !! swift
// Swift 解答
func findIntersection(_ arr1: [Int], _ arr2: [Int]) -> [Int] {
    let set1 = Set(arr1)
    let set2 = Set(arr2)
    return Array(set1.intersection(set2))
}

// 测试
let array1 = [1, 2, 3, 4, 5]
let array2 = [3, 4, 5, 6, 7]
print(findIntersection(array1, array2)) // [3, 4, 5]
```
</UniversalEditor>

## 关键要点

### Swift 集合优势
1. **类型安全**：所有集合都是强类型的，防止运行时错误
2. **性能**：优化的实现，具有可预测的性能特征
3. **函数式编程**：丰富的数据转换函数式方法
4. **内存安全**：使用 ARC 自动内存管理
5. **值语义**：数组和字典是值类型，提供可预测的行为

### 与 JavaScript 的关键差异
1. **类型系统**：Swift 集合需要显式或推断的类型
2. **可变性**：Swift 区分可变和不可变集合
3. **性能**：Swift 集合具有更可预测的性能特征
4. **内存管理**：自动引用计数 vs 垃圾回收
5. **值类型**：Swift 数组和字典是值类型，不是引用类型

### 最佳实践
1. **使用适当的集合类型**来满足数据需求
2. **利用函数式编程方法**进行数据转换
3. **考虑性能影响**选择操作
4. **尽可能使用类型推断**编写更清晰的代码
5. **为大型集合预分配容量**
6. **对大型数据转换使用惰性求值**

### 下一步
在下一个模块中，我们将探索 Swift 中的控制流，包括条件语句、循环和 switch 语句的模式匹配。这将建立在我们对集合的理解基础上，展示如何在 Swift 中有效地处理数据。 